/**
 * 
 */
package de.cbf.cam.dsa.tavern.carte.ui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeNodeContentProvider;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;

import de.cbf.cam.dsa.tavern.carte.model.entities.Ingredient;
import de.cbf.cam.dsa.tavern.carte.model.entities.interfaces.IEditableEntity;
import de.cbf.cam.dsa.tavern.carte.ui.interfaces.ICompositeEntities;
import de.cbf.cam.dsa.tavern.carte.ui.interfaces.ICompositePersistEntity;

/**
 * @author Stefan Alter
 *
 */
public class CompositeIngredients extends Composite implements ICompositePersistEntity,ICompositeEntities {

	public static final int FILTER_TYPE_STANDARD = 1;
	public static final int FILTER_TYPE_CATEGORY = 2;	
	private static final String NO_FILTER = "<kein Filter>";  //  @jve:decl-index=0:
	
	private Combo comboFilter;
	private Tree treeIngredients;
	private TreeViewer treeViewer;
	private ViewerFilterIngredient ingredientTypeFilter; 	
	
	private Set<String>     		categoryTypes 	= new HashSet<String>();  //  @jve:decl-index=0:
	
	private int filterType = 0;

	/**
	 * @param parent
	 * @param style
	 */
	public CompositeIngredients(Composite parent, int style) {
		super(parent, style);
		initialize();			
	}

	/**
	 * This method initializes this
	 * 
	 */
	private void initialize() {
        GridLayout gridLayout = new GridLayout();
	        gridLayout.marginWidth		= 0;
	        gridLayout.marginHeight		= 0;
	        gridLayout.verticalSpacing 	= 0;
	    //    
        GridData gDTreeIngredients = new GridData();
			gDTreeIngredients.horizontalAlignment		= GridData.FILL;
			gDTreeIngredients.verticalAlignment			= GridData.FILL;
			gDTreeIngredients.grabExcessHorizontalSpace	= true;
			gDTreeIngredients.grabExcessVerticalSpace	= true;
		//	
		this.setLayout(gridLayout);
		//	
		comboFilter = new Combo(this,SWT.DROP_DOWN);
		comboFilter.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		comboFilter.setText(NO_FILTER);
			
        treeIngredients = new Tree(this, SWT.BORDER|SWT.MULTI);
		treeIngredients.setHeaderVisible(true);
		treeIngredients.setLayoutData(gDTreeIngredients);
		treeIngredients.setLinesVisible(true);	
		treeIngredients.addSelectionListener(new IngredientSelection());
		treeIngredients.addMouseListener(new IngredientDoubleClick());
			TreeColumn columnIngredient = new TreeColumn(treeIngredients, SWT.NONE);
			columnIngredient.setWidth(195);
			columnIngredient.setText("Zutat");
			TreeColumn columnBasePrice = new TreeColumn(treeIngredients, SWT.NONE);
			columnBasePrice.setWidth(38);
			columnBasePrice.setText("Preis");
			
		treeViewer = new TreeViewer(treeIngredients);
		treeViewer.setContentProvider(new TreeNodeContentProvider(){
			@Override
			public Object[] getChildren(Object parentElement) {
				if (parentElement instanceof Ingredient) {
					Ingredient ingredient = (Ingredient) parentElement;
					Collection<Object> result = new HashSet<Object>();					
					for (Iterator<Ingredient> iterator = ingredient.iteratorSubIngredients(); iterator.hasNext();) {
						Ingredient subIngredient = (Ingredient) iterator.next();	
						result.add(subIngredient);
					}
					return result.toArray();
				}				
				return super.getChildren(parentElement);
			}

			@Override
			public boolean hasChildren(Object element) {
				if (element instanceof Ingredient) {
					Ingredient ingredient = (Ingredient) element;
					boolean result = false;
					if(ingredient.iteratorSubIngredients().hasNext()){
						result = true;
					}
					return result;
				}
				return false;
			}

			@Override
			public Object getParent(Object element) {
				if (element instanceof Ingredient) {
					Ingredient ingredient = (Ingredient) element;
					return ingredient.getParent();
				}
				return null;
			}
			
			@SuppressWarnings("rawtypes")
			@Override
			public Object[] getElements(Object inputElement) {
				if (inputElement instanceof List) {
					List list = (List) inputElement;
					return list.toArray();
				}
				return null;
			}

			@Override
			public void inputChanged(Viewer viewer, Object oldInput,
					Object newInput) {				
				super.inputChanged(viewer, oldInput, newInput);
			}
			
		});
		treeViewer.setLabelProvider(new ITableLabelProvider(){
			@Override
			public void addListener(ILabelProviderListener listener) {				
			}

			@Override
			public void dispose() {				
			}

			@Override
			public boolean isLabelProperty(Object element, String property) {				
				return true;
			}

			@Override
			public void removeListener(ILabelProviderListener listener) {				
			}

			@Override
			public Image getColumnImage(Object element, int columnIndex) {
				//little ingredient images?
				return null;
			}

			@Override
			public String getColumnText(Object element, int columnIndex) {
				if (element instanceof Ingredient) {
					Ingredient ingredient = (Ingredient) element;
					switch (columnIndex) {
						case 0:{
							return ingredient.getName();
						}
						case 1:{
							//write only real prices
							Double number = ingredient.getBasePrice();
							if(number != 0.0){
								String result = NumberFormat.getNumberInstance().format(number);
								return result;
							}else{
								return "";
							}							
						}
					}
				}
				return "NoLabel";
			}			
		});		
		//
		registerListeners();
	}
	private void registerListeners() {
		comboFilter.addSelectionListener(new FilterSelectionListener());	
		treeIngredients.addKeyListener(delete);		
	}

	/** recursive deep search for categories
	 * @param ingredient
	 */
	private void addCategoryTypes(Ingredient ingredient){	
		Iterator<String> iteratorCategoryTypes = ingredient.iteratorCategoryTypes(); 
		while (iteratorCategoryTypes.hasNext()) {
			String categoryType = (String) iteratorCategoryTypes.next();
			this.categoryTypes.add(categoryType);//because set it will automatically ignore doubles	
		}
		Iterator<Ingredient> iteratorIngredients = ingredient.iteratorSubIngredients();
		while (iteratorIngredients.hasNext()) {
			Ingredient subIngredient = (Ingredient) iteratorIngredients.next();
			addCategoryTypes(subIngredient);
		}		
	}	
	
	public void  deSelectAll(){
		treeViewer.setSelection(null);		
	}
	
	public Ingredient getSelectedObject(){
		ISelection selection = treeViewer.getSelection();
		if (selection instanceof TreeSelection) {
			TreeSelection treeSelection = (TreeSelection) selection;
			Object item = treeSelection.getFirstElement();
			if (item instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) item;				
				return ingredient;								
			}			
		}		
		return null;
	}
	@Override
	public IEditableEntity getSelectedEntity() {
		if (getSelectedObject() instanceof IEditableEntity) {
			IEditableEntity entity = (IEditableEntity) getSelectedObject();
			return entity;
		}
		return null;
	}
	
	@Override
	public Object[] getSelectedObjects() {
		ISelection selection = treeViewer.getSelection();		
		if (selection instanceof TreeSelection) {
			TreeSelection treeSelection = (TreeSelection) selection;
			Collection<Object> resultList = new ArrayList<Object>();
			@SuppressWarnings("unchecked")
			Iterator<Object> iterator = treeSelection.iterator();
			while (iterator.hasNext()) {
				Object object = (Object) iterator.next();
				resultList.add(object);
			}
			return resultList.toArray();
		}		
		return null;
	}
	@Override
	public IEditableEntity[] getSelectedEntities() {
		Object[] objects = getSelectedObjects();
		IEditableEntity[] resultArray = new IEditableEntity[0];
		java.util.List<IEditableEntity> resultList = new ArrayList<IEditableEntity>();
		for (int i = 0; i < objects.length; i++) {
			if (objects[i] instanceof IEditableEntity) {
				IEditableEntity entity = (IEditableEntity) objects[i];
				resultList.add(entity);
			}
		}
		return resultList.toArray(resultArray);
	}	
	
	@Override
	public void setSelectedEntity(IEditableEntity value) throws IllegalArgumentException {
		if (value instanceof Ingredient) {
			Ingredient ingredient = (Ingredient) value;
			setSelectedIngredient(ingredient);
		}		
	}
	
	public void setSelectedIngredient(Ingredient ingredient) {
		treeViewer.setSelection(new StructuredSelection(ingredient), true);		
	}
	
	public void setObjects(Iterator<? extends Object> iterator){
		List<Ingredient> ingredients = new ArrayList<Ingredient>();
		while (iterator.hasNext()) {
			Ingredient ingredient = (Ingredient) iterator.next();
			addCategoryTypes(ingredient);
			ingredients.add(ingredient);
		}		
		List<String> typeList = new ArrayList<String>();
		typeList.add(NO_FILTER);
		typeList.addAll(categoryTypes);
		String[] types = typeList.toArray(new String[0]);		
		comboFilter.setItems(types);		
		comboFilter.select(0);
		treeViewer.setInput(ingredients);
		treeViewer.refresh();
	}
	@Override
	public void setEntities(Iterator<IEditableEntity> iterator) {
		setObjects(iterator);		
	}
	
	/** only possible if Standard filter is set
	 * @param iterator
	 */
	public void filterIngredientsByTypes(Iterator<String> iterator){
		if(ingredientTypeFilter == null) return;
		if(iterator == null){ //indicates NO_FILTER
			ingredientTypeFilter.setTypes(new ArrayList<String>());
			comboFilter.setText(NO_FILTER);			
		}else{		
			//set combo text
			StringBuffer buffer = new StringBuffer();
			Collection<String> types = new HashSet<String>(); 
			while (iterator.hasNext()) {
				String type = (String) iterator.next();
				types.add(type);
				buffer.append(type);
				if(iterator.hasNext()){
					buffer.append(", ");
				}	
			}			
			ingredientTypeFilter.setTypes(types);
			comboFilter.setText(buffer.toString());
		}
		//refresh viewer
		treeViewer.refresh();
	}
	
	public int getFilterType() {
		return filterType;
	}
	public void setFilterType(int type ){
		this.filterType = type;
		switch (type) {
			case FILTER_TYPE_CATEGORY:{
				treeViewer.setFilters(new ViewerFilter[0]);
				ingredientTypeFilter = null;
				treeViewer.addFilter(new ViewerFilterCategory());
				comboFilter.setEnabled(false);
				break;
			}
			case FILTER_TYPE_STANDARD:{
				treeViewer.setFilters(new ViewerFilter[0]);
				ingredientTypeFilter = new ViewerFilterIngredient();
				treeViewer.addFilter(ingredientTypeFilter);
				comboFilter.setEnabled(true);
				break;
			}
		}
	}
	
	
	private List<ActionListener> ingredientSelectionListeners = new ArrayList<ActionListener>();  //  @jve:decl-index=0:
	public boolean registerSelectionListener(ActionListener listener){
		boolean result = ingredientSelectionListeners.add(listener);				
		return result;
	}
	public boolean deRegisterSelectionListener(ActionListener listener){
		boolean result = ingredientSelectionListeners.remove(listener);		
		return result;
	}	
	private class IngredientSelection extends SelectionAdapter{
		@Override
		public void widgetSelected(SelectionEvent e) {
			ActionEvent event = new ActionEvent(CompositeIngredients.this, ActionEvent.ACTION_PERFORMED,"ingredientSelection");
			for (Iterator<ActionListener> iterator = ingredientSelectionListeners.iterator(); iterator.hasNext();) {
				ActionListener listener = iterator.next();
				listener.actionPerformed(event);				
			}
		}		
	}
	private List<ActionListener> ingredientDoubleClickListeners = new ArrayList<ActionListener>();  //  @jve:decl-index=0:
	public boolean registerDoubleClickListener(ActionListener listener){
		boolean result = ingredientDoubleClickListeners.add(listener);				
		return result;
	}
	public boolean deRegisterDoubleClickListener(ActionListener listener){
		boolean result = ingredientDoubleClickListeners.remove(listener);		
		return result;
	}		
	private class IngredientDoubleClick extends MouseAdapter{		
		@Override
		public void mouseDoubleClick(MouseEvent e) {
			ActionEvent event = new ActionEvent(CompositeIngredients.this, ActionEvent.ACTION_PERFORMED,"ingredientDoubleClick");
			for (Iterator<ActionListener> iterator = ingredientDoubleClickListeners.iterator(); iterator.hasNext();) {
				ActionListener listener = iterator.next();
				listener.actionPerformed(event);				
			}
		}		
	}
	
	private Collection<ActionListener> deleteListeners = new ArrayList<ActionListener>();
	public boolean registerDeleteListener(ActionListener listener){
		return deleteListeners.add(listener);
	}
	public boolean deRegisterDeleteListener(ActionListener listener){
		return deleteListeners.remove(listener);
	}
	private DeleteAdvancer delete = new DeleteAdvancer();
	private class DeleteAdvancer extends KeyAdapter{
		@Override
		public void keyPressed(KeyEvent e) {
			if(e.keyCode == SWT.DEL){
				ActionEvent event = new ActionEvent(CompositeIngredients.this, ActionEvent.ACTION_PERFORMED, "deleteIngredient");
				for (ActionListener listener : deleteListeners) {
					listener.actionPerformed(event);
				}
			}			
		}		
	}
	
	@Override
	public boolean registerListChangedListener(ActionListener listener) {		
		return false;
	}

	@Override
	public boolean deRegisterListChangedListener(ActionListener listener) {		
		return false;
	}	
	
	private class FilterSelectionListener extends SelectionAdapter{
		@Override
		public void widgetSelected(SelectionEvent e) {
			if (e.widget instanceof Combo) {
				Combo comboFilter = (Combo) e.widget;
				String filter = comboFilter.getText();
				if(filter.equals(NO_FILTER)){
					filterIngredientsByTypes(null);
				}else{
					List<String> filterList = new ArrayList<String>();
					filterList.add(filter);
					filterIngredientsByTypes(filterList.iterator());
				}				
			}			
		}		
	}
	
	private class ViewerFilterIngredient extends ViewerFilterCategory{
		private Collection<String> types = new HashSet<String>();
		@Override
		public boolean select(Viewer viewer, Object parentElement,Object element) {
			if(types.size() < 1){//no filter set, go unfiltered
				return true;
			}			
			if (element instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) element;
				if((ingredient.getType()&Ingredient.TYPE_Category)!=0){
					return true;
				}
				for (Iterator<String> iterator = ingredient.iteratorCategoryTypes(); iterator.hasNext();) {
					String type = (String) iterator.next();
					if(types.contains(type)){
						return true;
					}					
				}
			}
			return false;
		}
		public void setTypes(Collection<String> types) {
			this.types.clear();
			this.types.addAll(types);			
		}		
	}
	private class ViewerFilterCategory extends ViewerFilter{
		@Override
		public boolean select(Viewer viewer, Object parentElement,Object element) {
			if (element instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) element;
				if((ingredient.getType()&Ingredient.TYPE_Category)!=0){
					return true;
				}			
			}
			return false;
		}		
	}

	
	
	
	
	
	
}  //  @jve:decl-index=0:visual-constraint="10,10"
